#pragma once

#include <sys/epoll.h>
#include <unordered_map>

#include <unistd.h>

#include "Logger.hpp"
#include "Channel.hpp"

using namespace std;
using namespace LogMudule;


//对任意fd关心的事件进行管理，通过封装epoll实现
class Epoller
{
    static const uint32_t DefaultReventsMaxSize=256;
private:
    // epoll模型对应的fd
    int _epollfd;
    // 调用epoll_wait获取就绪连接时，存储获取到的就绪fd及其就绪事件
    struct epoll_event _revents[DefaultReventsMaxSize];
    // 监控的所有事件（Channel）  //注意Channel *指向的并非堆区对象
    unordered_map<int, Channel *> _channels;

    static int CreateEpoll()
    {
        //创建epoll模型
        int epollfd=epoll_create(256);
        if(epollfd<0)
        {
            LOG(LogLevel::DEADLY)<<"Epoller：epoll_create失败!,epollfd损坏";
            abort();
        }
        return epollfd;
    }
    //封装epoll_ctl系统调用，op为操作方式
    void Update(Channel*channel, int op)
    {
        int fd=channel->Fd();

        //填充字段
        struct epoll_event event;
        event.data.fd=fd;
        event.events=channel->Events();

        int ret=epoll_ctl(_epollfd,op,fd,&event);
        if(ret<0)
        {
            LOG(LogLevel::DEADLY)<<"Epoller：epoll_ctl失败!,epollfd损坏,fd: "<< fd;
            abort();
        }
    }
public:
    Epoller()
        : _epollfd(CreateEpoll())
    {
    }

    //判断是否已经监控了某个Channel对象
    bool HasChannel(Channel*chnnel)
    {
        int fd=chnnel->Fd();
        //如果没找到
        if(_channels.find(fd)==_channels.end())
        {
            return false;
        }
        return true;
    }
    
    //新增/修改指定Channel关心的事件，Channel已经监控了就修改，没有监控就添加
    void UpdateEvent(Channel *channel)
    {
        //如果存在
        if(HasChannel(channel)==true)
        {
            //就更新监控事件
            Update(channel,EPOLL_CTL_MOD);
        }
        else//否则
        {
            //就添加监控事件
            Update(channel,EPOLL_CTL_ADD);
            //添加到哈希表
            _channels.insert({channel->Fd(),channel});
        }
    }
    //删除指定的Channel关心的事件，并在哈希表（_channels）中将其删除
    void RemoveEvent(Channel *channel)
    {
        //如果存在
        if(HasChannel(channel)==true)
        {
            LOG(LogLevel::DEBUG)<<"Epoller：移除fd: "<<channel->Fd()<<"事件监控";

            //删除指定的Channel关心的事件
            Update(channel,EPOLL_CTL_DEL);
            //在哈希表（_channels）中将其删除
            _channels.erase(channel->Fd());
        }
        else
        {
            LOG(LogLevel::DEBUG)<<"Epoller：试图移除不存在的Channel对象";
        }
    }
    //开始所有fd关心事件的监控（也就是调用epoll_wait），并返回所有就绪的Channel
    void Poll(vector<Channel*> *chs)
    {
        int n=epoll_wait(_epollfd,_revents,DefaultReventsMaxSize,-1);
        if(n<0)
        {
            //如果被信号中断
            if(errno==EINTR)
            {
                LOG(LogLevel::DEBUG)<<"Epoller：epoll_wait被信号中断";
                return;
            }
            LOG(LogLevel::DEADLY)<<"Epoller：epoll_wait失败!,epollfd损坏";
            abort();
        }
        //遍历就绪的fd
        for(int i=0;i<n;i++)
        {
            //取出就绪事件和就绪fd
            int rfd=_revents[i].data.fd;
            uint32_t revents=_revents[i].events;

            //就绪fd必须是哈希表中管理的，否则就有问题
            if(_channels.find(rfd)==_channels.end())
            {
                LOG(LogLevel::DEADLY)<<"Epoller：不被管理的fd就绪了,代码逻辑出现问题!! fd: "<<rfd;
                abort();
            }

            //把就绪事件设置给Channel对象
            _channels[rfd]->SetRevents(revents);

            //把就绪事件Channel对象添加
            chs->emplace_back(_channels[rfd]);
        }
    }
    ~Epoller()
    {
        ::close(_epollfd);
    }
};